#!/usr/bin/python

import os, sys, subprocess, signal, threading, time, datetime, uuid,zipfile
from subprocess import *
from threading import *

from ivic.core import Logging
from ivic.core.Utils import GenUUID

from ivic.vprocess.Config import *
from ivic.vprocess.Software import *
from ivic.vprocess.UnionMount import *
from ivic.vprocess.MountUtils import *
from ivic.vprocess.VprocessMonitor import *

from ivic.vprocess.ServerMonitor import *

logger = Logging.get_logger('ivic.vprocess.Vprocess')
    
class VProcess(Thread):
    
    def __init__(self, user, softname, display, config=None, args=""):
        
        Thread.__init__(self)
        self.uuid = GenUUID()
        
        self.display = display
        self.user = user
        self.pid = -1
        self.softname=softname
        if config != None: self.config = config
        else: self.config = VProcessConfig()
        
        self.software = retrieve(softname, self.config) 
        
        self.args = args or ""
        
        self.state = 'inited'
        self.union_point = None
#        self.setName("Thread-%s-invoker" % softname)
        self.stime = {'start':0.0, 'exit':0.0}
        self.info = None
        self.server_uuid =  vprocess_config.uuid
        
        self.reporter = VprocessReporter.Instance()
        
    def __union(self):
        try:
            if self.state == 'inited' or self.state == 'clean':
                config = self.config.transfer
                uconf = self.config.union
                vsfs_union(uconf, self.user, config.urlprefix, self.software.name, '1000',uconf.ro,uconf.rw)
        except Exception, e:
            raise e
    
    def enable_gconf(self):
        command = "gconf --spawn"
        p = subprocess.Popen(command,shell=True)
        p.wait()    
           
    def start(self):
        try:
            self.__union()
            self.state = 'united'
        except Exception, e:
            logger.error("%s" % e)
            return

     	#pre exec#
	try:
	    self.pre(self.softname)
	    logger.debug("pre_exec")
	except Exception, e:
	    logger.error("pre_script wrong")
	


        try:
            if self.state == 'united':
#****************the fork and exec with environment version********************#                              
                pid = os.fork()           
                if pid:
                    self.pid = pid
                    self.state = "running"
                    logger.debug('subprocess:%d created.' % pid)
                else:
                    #logger.debug("enter forked process.")
                    
                    command = self.software.exec_cmd
                    command = command + " " + self.args
                    logger.debug('original command, should be ascii')
                    logger.debug(command)
                    
                    # convert to utf-8
                    command = unicode(command)
                    command = command.encode('utf-8')
                    logger.debug('utf-8 command')
                    logger.debug(command)
                    
                    fake_root = self.config.union.root + "/" + self.user + "/" + self.software.name
                    logger.debug("chroot :%s" % fake_root)
                    #why need os.chdir()?
#                    os.chdir(fake_root)
                    os.chroot(fake_root)
                    
                    #will suspend if ivic-daemon does'nt start with root.
                    #os.system("su")
                    #enable gconfd for the process
                    #self.enable_gconf()
                    os.system("gconftool --spawn")
                    
                    try:
                        #used for firefox PATH environ
                        os.environ["PATH"]=":".join(["/usr/bin/ivic-firefox",os.environ["PATH"]])
                        
                        home = "/home/" + self.user
                        if self.user in('root', 'root'.encode('ascii', 'ignore')):
                            os.environ['HOME'] = '/root'  
                        elif os.path.exists(home):
                            os.environ['HOME'] = home 
                        
                        #force to be home
                        if not os.environ["HOME"] in (home, '/root'):
                            os.environ["HOME"] = '/home'
                        
                        os.chdir(os.environ["HOME"])
                        os.environ["PWD"] = os.environ["HOME"]
                        logger.debug(os.environ["HOME"])
                    except Exception, e:
                        logger.error("[sethome] %s" % e)
                               
                    os.environ['DISPLAY'] = self.display
                    logger.debug("DISPLAY = %s" % self.display)
                    #when can we remove this? 
                    os.environ['SOFTNAME'] = self.software.name
                    logger.debug("SOFTNAME = %s" % self.software.name)
                            
                    try:
                        os.execve("/bin/sh", ('sh', '-c', command), os.environ)
                        os._exit(0)
                    except Exception, e:
                        self.pid = -1
                        self.state = "error"
                        logger.error("%s" % e)
        except Exception, e:
            self.pid = -1
            self.state = "error"
            logger.error("%s" % e)
        
        #we poll the state here to get the start time
        self.poll()
        if self.state == "running": 
            self.stime = {'start':time.time(), 'exit':0.0} 
            self.reporter.putEvent("START", self.dict())
    def getZip(self,softname,state):
        import yaml
        conf = {}
        try:
            conf = yaml.load(file('/etc/ivic/vprocess.yaml'))
        except Exception, e:
            logger.error("%s" % e)
            
        getUrl=conf['transfer']['urlprefix']

        url="%s%s/%s.zip"%(getUrl,state,softname)
	#logger.debug("url=%s"%url)
        urllib.urlretrieve(url,"%s.zip"%softname)
        


    def pre(self,softname,argv=["name","date","fee"]):
        self.getZip(softname,"pre_exec")
	zipname="%s.zip"%softname
        #extracting the zip file downloaded.
        try:
            a=zipfile.ZipFile(zipname,"r")
        except Exception, e:
            logger.debug("no pre_exec,%s"%e)
            os.system("rm %s.zip"%softname)
            return
	self.unZip(softname,"pre")
	self.rmZip(softname,"pre")

    def after(self,softname,argv=["name","date","fee"]):
	self.getZip(softname,"after_exec")
	zipname="%s.zip"%softname
	#extracting the zip file downloaded.
        try:
            a=zipfile.ZipFile(zipname,"r")
        except Exception, e:
            logger.debug("no after_exec,%s"%e)
            os.system("rm %s.zip"%softname)
            return
        self.unZip(softname,"after")
	self.rmZip(softname,"after")

    def rmZip(self,softname,status):
	os.chdir("/")
	if not os.path.exists("temp"):
            return
        #else:
            #print "temp exits"
	os.chdir("/temp/%s"%status)
	os.system("rm -r %s"%softname)
	os.chdir("/")

    def unZip(self,softname,status):
	zipname="%s.zip"%softname
	os.chdir("/")
        if not os.path.exists("temp"):
            os.system("mkdir temp")
        #else:
            #print "temp exits"
        os.system("mv %s /temp/"%zipname)
	os.chdir("%stemp/"%os.getcwd())
	if not os.path.exists("%s"%status):
	    os.system("mkdir %s"%status)
	os.system("mv %s /temp/%s"%(zipname,status))
	os.chdir("/temp/%s"%status)
	os.system("unzip %s"%zipname)
	os.system("rm %s"%zipname)
	os.chdir("%s/%s"%(os.getcwd(),softname))
        #os.system("run.sh %s %s %s"%(argv[0],argv[1],argv[2]))
	try:
	    #os.system("sh run.sh")#should block here until the script done.
            logger.debug("If log blocked here, check the script")
            pipe=os.popen("sh run.sh","r")
            result=pipe.readlines()
            pipe.close()
	    logger.debug("%s_exec run.sh %s "%(status,result))
	except Exception,e:
	    logger.error("%s_exec run.sh ERROR"%status)
        os.chdir("/")
        
    def run(self):
        self.start()
        
    def clean(self):
        try:
            if self.state in ('stopped', 'united'):
                point = '/'.join([self.union_point, 'home', '*'])
                try: 
                    umount(point)
                    logger.debug(point) 
                except Exception, e: logger.error('%s' % e)
                
                point = '/'.join([self.union_point, '*'])
                try: 
                    umount(point)
                    logger.debug(point) 
                except Exception, e: logger.error('%s' % e)
                
                try:
                    umount(self.union_point)
                except Exception, e: logger.error('%s' % e)
                
        except Exception, e:
            logger.error("%s" % e)
    
    def poll(self):
        try:
            if self.state == 'running':
                try:
                    result = os.waitpid(self.pid, os.WNOHANG)
                    if result[0] == self.pid: self.state = "stopped"
                except Exception, e:
                    self.state = "stopped"
                    logger.error("%s" % e)
                
                if self.state == "stopped":
                    self.stime['exit'] = time.time()

		    #after exec#
	            try:
           	        self.after(self.softname)
            		logger.debug("after_exec")
        	    except Exception, e:
            		logger.error("after_script wrong")



                    self.trigger("STOP")
                elif self.state == 'running':
                    self.trigger("RUNNING")
                    
        except Exception, e:
            logger.error("%s" % e) 
    
    #This way is a litter rude.
    def kill(self):
        try:
            if self.pid > 0:
                os.kill(self.pid, signal.SIGKILL)
            self.poll()
        except Exception, e:
            logger.error("%s" % e)
            
    def dict(self):
        try:
            info = {'uuid':self.uuid,
                    'pid':self.pid,
                    'app':self.software.name,
                    'state':self.state,
                    'vspace':self.display,
                    'user':self.user,
                    'server':self.server_uuid} 
            ISOTIMEFORMAT = "%Y-%m-%d %X"
            info['start'] = time.strftime(ISOTIMEFORMAT, time.localtime(self.stime['start']))
            if self.state == 'stopped':
                info['exit'] = time.strftime(ISOTIMEFORMAT, time.localtime(self.stime['exit']))
            return info
        except Exception, e:
            logger.error("[dict]%s" % e)
            return {}        
        
    
    def status(self):
        try:
            return self.dict()
        except Exception, e:
            logger.error("%s" % e)
            return {}
    
    def __str__(self):
        info = self.status()
        return repr(info)
    
    def trigger(self, eventType):
        try:
            self.reporter.putEvent(eventType, self.dict())
#            logger.debug("%s\t%s" % (eventType, self.dict()))
        except Exception, e:
           logger.error("%s" % e)
          
def create(user, softname, display, config=None, args=""):
    try:
        vprocess = VProcess(user, softname, display, config, args)
        return vprocess
    except Exception, e:
        logger.error("%s" % e) 
        raise

#for safe test only
#if __name__ == "__main__":
#    
#    exec_dir = os.path.split(sys.argv[0])[0]
#
#    if exec_dir in ('/usr/sbin', '/usr/bin'):
#      from ivic.vprocess.Config import *
#      from ivic.vprocess.Software import *
#      from ivic.vprocess.UnionMount import *
#      from ivic.vprocess.MountUtils import *
#    else:
#      sys.path.insert(0, os.getcwd())
#      from ivic.vprocess.Config import *
#      from ivic.vprocess.Software import *
#      from ivic.vprocess.UnionMount import *
#      from ivic.vprocess.MountUtils import *
#      ivic.core.set_rundebug()
#      
#    softname = sys.argv[1]
#    user = sys.argv[2]
#    vspace = sys.argv[3]
#        
#    try:
#        vproc = create(user, softname, vspace)
#        vproc.start()
#        print str(vproc)
#            
#    except Exception,e:
#        print "run %s failed" % softname
#        print e
